package eva

import (
	"testing"
	"gitcode.com/deyiyangyang/gascheme/lex"
	"gitcode.com/deyiyangyang/gascheme/obj"
	"gitcode.com/deyiyangyang/gascheme/par"
)

// TestBeginWithSingleExpression tests that begin with a single expression returns its value
func TestBeginWithSingleExpression(t *testing.T) {
	input := "(begin 42)"
	
	lexer := lex.NewLexer(input)
	parser := par.NewParser(lexer)
	expr, err := parser.ParseExpression()
	if err != nil {
		t.Fatalf("parser error: %s", err)
	}
	
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)
	
	result := Eval(expr, env)
	
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Fatalf("expected integer, got %T", result)
	}
	
	if integer.Value != 42 {
		t.Errorf("expected 42, got %d", integer.Value)
	}
}

// TestBeginWithMultipleExpressions tests that begin evaluates expressions in sequence
func TestBeginWithMultipleExpressions(t *testing.T) {
	// This test uses set! to verify expressions are evaluated in sequence
	input := `
	(begin
		(define x 1)
		(set! x (+ x 1))
		(set! x (+ x 1))
		x)
	`
	
	lexer := lex.NewLexer(input)
	parser := par.NewParser(lexer)
	expr, err := parser.ParseExpression()
	if err != nil {
		t.Fatalf("parser error: %s", err)
	}
	
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)
	
	result := Eval(expr, env)
	
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Fatalf("expected integer, got %T", result)
	}
	
	if integer.Value != 3 {
		t.Errorf("expected 3, got %d", integer.Value)
	}
}

// TestBeginReturnsLastValue tests that begin returns the value of the last expression
func TestBeginReturnsLastValue(t *testing.T) {
	input := `
	(begin
		(+ 1 2)
		(+ 3 4)
		(+ 5 6))
	`
	
	lexer := lex.NewLexer(input)
	parser := par.NewParser(lexer)
	expr, err := parser.ParseExpression()
	if err != nil {
		t.Fatalf("parser error: %s", err)
	}
	
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)
	
	result := Eval(expr, env)
	
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Fatalf("expected integer, got %T", result)
	}
	
	if integer.Value != 11 {
		t.Errorf("expected 11, got %d", integer.Value)
	}
}

// TestBeginWithNoExpressions tests error handling for begin with no expressions
func TestBeginWithNoExpressions(t *testing.T) {
	input := "(begin)"
	
	lexer := lex.NewLexer(input)
	parser := par.NewParser(lexer)
	expr, err := parser.ParseExpression()
	if err != nil {
		t.Fatalf("parser error: %s", err)
	}
	
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)
	
	result := Eval(expr, env)
	
	_, ok := result.(*obj.Error)
	if !ok {
		t.Fatalf("expected error, got %T", result)
	}
}

// TestBeginInNestedContext tests that begin works correctly in nested expressions
func TestBeginInNestedContext(t *testing.T) {
	input := `
	(+ 1 
	   (begin 
	     (define x 5) 
		 (set! x (+ x 5)) 
		 x))
	`
	
	lexer := lex.NewLexer(input)
	parser := par.NewParser(lexer)
	expr, err := parser.ParseExpression()
	if err != nil {
		t.Fatalf("parser error: %s", err)
	}
	
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)
	
	result := Eval(expr, env)
	
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Fatalf("expected integer, got %T", result)
	}
	
	if integer.Value != 11 {
		t.Errorf("expected 11, got %d", integer.Value)
	}
}

// TestBeginWithBooleanValues tests that begin correctly handles boolean values
func TestBeginWithBooleanValues(t *testing.T) {
	input := `
	(begin
	  (> 5 3)
	  (< 2 4)
	  (= 1 1))
	`
	
	lexer := lex.NewLexer(input)
	parser := par.NewParser(lexer)
	expr, err := parser.ParseExpression()
	if err != nil {
		t.Fatalf("parser error: %s", err)
	}
	
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)
	
	result := Eval(expr, env)
	
	boolean, ok := result.(*obj.Boolean)
	if !ok {
		t.Fatalf("expected boolean, got %T", result)
	}
	
	if boolean.Value != true {
		t.Errorf("expected true, got %t", boolean.Value)
	}
}

// TestBeginWithInnerBegin tests nested begin expressions
func TestBeginWithInnerBegin(t *testing.T) {
	input := `
	(begin
	  (define x 1)
	  (begin
	    (set! x (+ x 1))
		(set! x (+ x 1)))
	  x)
	`
	
	lexer := lex.NewLexer(input)
	parser := par.NewParser(lexer)
	expr, err := parser.ParseExpression()
	if err != nil {
		t.Fatalf("parser error: %s", err)
	}
	
	env := obj.NewGlobalEnvironment()
	RegisterBuiltinProcedures(env)
	
	result := Eval(expr, env)
	
	integer, ok := result.(*obj.Integer)
	if !ok {
		t.Fatalf("expected integer, got %T", result)
	}
	
	if integer.Value != 3 {
		t.Errorf("expected 3, got %d", integer.Value)
	}
}